import {expect} from "assertions"
import * as sinon from "sinon"

import {ColumnDataSource} from "@bokehjs/models/sources/column_data_source"
import {GlyphRenderer} from "@bokehjs/models/renderers/glyph_renderer"
import {LegendItem} from "@bokehjs/models/annotations/legend_item"
import {logger} from "@bokehjs/core/logging"

describe("LegendItem", () => {

  describe("initialize", () => {
    let logger_stub: sinon.SinonStub

    before_each(() => {
      logger_stub = sinon.stub(logger, "error")
    })

    after_each(() => {
      logger_stub.restore()
    })

    it("should log an error if _check_data_sources_on_renderers is false", () => {
      const stub = sinon.stub(LegendItem.prototype, "_check_data_sources_on_renderers").returns(false)
      new LegendItem()
      sinon.assert.calledOnce(logger_stub)
      stub.restore()
    })

    it("should log an error if _check_field_label_on_data_source is false", () => {
      const stub = sinon.stub(LegendItem.prototype, "_check_field_label_on_data_source").returns(false)
      new LegendItem()
      sinon.assert.calledOnce(logger_stub)
      stub.restore()
    })
  })

  describe("_check_data_sources_on_renderers", () => {

    it("should return false if field label and different data sources", () => {
      const gr_1 = new GlyphRenderer({data_source: new ColumnDataSource()})
      const gr_2 = new GlyphRenderer({data_source: new ColumnDataSource()})
      const legend_item = new LegendItem({
        label: {field: "label"},
        renderers: [ gr_1, gr_2 ],
      })
      expect(legend_item._check_data_sources_on_renderers()).to.be.false
    })

    it("should return false if field label and no renderers", () => {
      const legend_item = new LegendItem({
        label: {field: "label"},
        renderers: [ ],
      })
      expect(legend_item._check_data_sources_on_renderers()).to.be.false
    })

    it("should return true if value label and different data sources", () => {
      const gr_1 = new GlyphRenderer({data_source: new ColumnDataSource()})
      const gr_2 = new GlyphRenderer({data_source: new ColumnDataSource()})
      const legend_item = new LegendItem({
        label: {value: "label"},
        renderers: [ gr_1, gr_2 ],
      })
      expect(legend_item._check_data_sources_on_renderers()).to.be.true
    })
  })

  describe("_check_field_label_on_data_source", () => {

    it("should return false if field label and label not in data source", () => {
      const gr_1 = new GlyphRenderer({
        data_source: new ColumnDataSource({data: {foo: [1]}}),
      })
      const legend_item = new LegendItem({
        label: {field: "label"},
        renderers: [ gr_1 ],
      })
      expect(legend_item._check_field_label_on_data_source()).to.be.false
    })

    it("should return false if field label and no renderers", () => {
      const legend_item = new LegendItem({
        label: {field: "label"},
        renderers: [ ],
      })
      expect(legend_item._check_field_label_on_data_source()).to.be.false
    })

    it("should return true if field label and label in data source", () => {
      const gr_1 = new GlyphRenderer({
        data_source: new ColumnDataSource({data: {label: [1]}}),
      })
      const legend_item = new LegendItem({
        label: {field: "label"},
        renderers: [ gr_1 ],
      })
      expect(legend_item._check_field_label_on_data_source()).to.be.true
    })
  })

  describe("get_field_from_label_prop", () => {

    it("should return undefined if label property is null", () => {
      const legend_item = new LegendItem({label: null})
      const field = legend_item.get_field_from_label_prop()
      expect(field).to.be.null
    })

    it("should return undefined if label property is value", () => {
      const legend_item = new LegendItem({label: {value: "milk"}})
      const field = legend_item.get_field_from_label_prop()
      expect(field).to.be.null
    })

    it("should return field if label property is field", () => {
      const legend_item = new LegendItem({label: {field: "milk"}})
      const field = legend_item.get_field_from_label_prop()
      expect(field).to.be.equal("milk")
    })
  })

  describe("get_labels_list_from_label_prop", () => {

    it("should return empty labels if visible is false", () => {
      const source = new ColumnDataSource({
        data: {
          label: ["foo", "bar", "foo", "bar"],
        },
      })
      const gr = new GlyphRenderer({data_source: source})
      const legend_item = new LegendItem({label: {field: "label"}, renderers: [gr], visible: false})
      const field = legend_item.get_labels_list_from_label_prop()
      expect(field).to.be.equal([])
    })

    it("should return labels if field is valid", () => {
      const source = new ColumnDataSource({
        data: {
          label: ["foo", "bar", "foo", "bar"],
        },
      })
      const gr = new GlyphRenderer({data_source: source})
      const legend_item = new LegendItem({label: {field: "label"}, renderers: [gr]})
      const field = legend_item.get_labels_list_from_label_prop()
      expect(field).to.be.equal(["foo", "bar"])
    })

    it("should return 'Invalid field' list if field is not in datasource", () => {
      const source = new ColumnDataSource({
        data: {
          x: [10, 20, 30, 40],
        },
      })
      const gr = new GlyphRenderer({data_source: source})
      const legend_item = new LegendItem({label: {field: "milk"}, renderers: [gr]})
      const field = legend_item.get_labels_list_from_label_prop()
      expect(field).to.be.equal(["Invalid field"])
    })

    it("should return 'No source found' list if no renderer and field used", () => {
      const legend_item = new LegendItem({label: {field: "milk"}, renderers: []})
      const field = legend_item.get_labels_list_from_label_prop()
      expect(field).to.be.equal(["No source found"])
    })

    it("should return value in single list if label is value", () => {
      const legend_item = new LegendItem({label: {value: "milk"}})
      const field = legend_item.get_labels_list_from_label_prop()
      expect(field).to.be.equal(["milk"])
    })

    it("should return empty list if label is null", () => {
      const legend_item = new LegendItem({label: null})
      const field = legend_item.get_labels_list_from_label_prop()
      expect(field).to.be.equal([])
    })
  })
})
